package com.treasure.mall.biz.service.core.order.impl;

import com.treasure.mall.biz.base.AbstractSearchableCrudService;
import com.treasure.mall.biz.base.AuthHelper;
import com.treasure.mall.biz.base.SpecificationAndSort;
import com.treasure.mall.biz.base.UserDetails;
import com.treasure.mall.biz.dto.BaseSaveDTO;
import com.treasure.mall.biz.dto.BaseSearchDTO;
import com.treasure.mall.biz.dto.condition.DeliverBillCondition;
import com.treasure.mall.biz.entity.embeddable.ReceivingInfo_;
import com.treasure.mall.biz.entity.order.DeliverBillPO;
import com.treasure.mall.biz.entity.order.DeliverBillPO_;
import com.treasure.mall.biz.entity.order.DeliverItemPO;
import com.treasure.mall.biz.entity.order.SubOrderPO;
import com.treasure.mall.biz.entity.user.MerchantPO_;
import com.treasure.mall.biz.remote.track.LogisticsProvider;
import com.treasure.mall.biz.remote.track.dto.ReceiveInfo;
import com.treasure.mall.biz.remote.track.dto.TrackInfoDTO;
import com.treasure.mall.biz.remote.track.enums.LogisticsStatus;
import com.treasure.mall.biz.repository.order.DeliverRepository;
import com.treasure.mall.biz.repository.order.OrderItemRepository;
import com.treasure.mall.biz.service.core.order.DeliverService;
import com.treasure.mall.biz.service.core.order.SubOrderService;
import com.treasure.mall.zbq.common.Jsr310Utils;
import com.treasure.mall.zbq.common.base.ApiResult;
import com.treasure.mall.zbq.common.base.ResultCodeEnum;
import com.treasure.mall.zbq.common.exception.ApiException;
import com.treasure.mall.zbq.common.ienum.order.OrderEnums;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.Predicate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

/**
 * @author allan
 * @version 1.0.0
 * @date 2020/6/12
 */
@Service
@Slf4j
public class DeliverServiceImpl extends AbstractSearchableCrudService<DeliverBillPO, String> implements DeliverService {
    @Autowired(required = false)
    private LogisticsProvider provider;
    private DeliverRepository deliverRepository;
    @Autowired
    private SubOrderService subOrderService;
    @Autowired
    private OrderItemRepository orderItemRepository;

    @Autowired
    protected DeliverServiceImpl(DeliverRepository repository) {
        super(repository);
        this.deliverRepository = repository;
    }

    @Override
    protected SpecificationAndSort<DeliverBillPO> specificationAndSort(BaseSearchDTO searchDTO) {
        DeliverBillCondition condition = (DeliverBillCondition) searchDTO;
        UserDetails userDetails = AuthHelper.getPrincipal();
        if (AuthHelper.isMerchant(userDetails)) {
            condition.setMerchantId(userDetails.getId());
        }

        Specification<DeliverBillPO> specification = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();

            if (!StringUtils.isEmpty(condition.getReceiver())) {
                predicates.add(cb.or(
                        cb.like(root.get(DeliverBillPO_.receivingInfo).get(ReceivingInfo_.userName), likeAll(condition.getReceiver())),
                        cb.like(root.get(DeliverBillPO_.receivingInfo).get(ReceivingInfo_.telNumber), likeAll(condition.getReceiver()))
                ));
            }

            if (condition.getReceived() != -1) {
                boolean isReceived = condition.getReceived() == 1;
                predicates.add(cb.equal(root.get(DeliverBillPO_.received), isReceived));
            }

            if (!StringUtils.isEmpty(condition.getExpressNo())) {
                predicates.add(cb.equal(root.get(DeliverBillPO_.expressNo), condition.getExpressNo()));
            }

            if (condition.getMerchantId() > 0) {
                predicates.add(cb.equal(root.get(DeliverBillPO_.merchantPO).get(MerchantPO_.merchantId), condition.getMerchantId()));
            }

            if (!StringUtils.isEmpty(condition.getId())) {
                predicates.add(cb.equal(root.get(DeliverBillPO_.id), condition.getId()));
            }
            if (!StringUtils.isEmpty(condition.getSubOrderId())) {
                predicates.add(cb.equal(root.get(DeliverBillPO_.subOrderId), condition.getSubOrderId()));
            }

            if (!StringUtils.isEmpty(condition.getBeginTime())) {
                LocalDateTime beginTime = Jsr310Utils.DateTime.of(condition.getBeginTime());
                predicates.add(cb.greaterThanOrEqualTo(root.get(DeliverBillPO_.createTime), beginTime));
            }
            if (!StringUtils.isEmpty(condition.getEndTime())) {
                LocalDateTime endTime = Jsr310Utils.DateTime.of(condition.getEndTime());
                predicates.add(cb.lessThanOrEqualTo(root.get(DeliverBillPO_.createTime), endTime));
            }
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };

        Sort sort = new Sort(Sort.Direction.DESC, "createTime");

        return new SpecificationAndSort<>(specification, sort);
    }

    @Override
    public DeliverBillPO save(BaseSaveDTO saveDTO) {
        return null;
    }

    @Override
    public List<TrackInfoDTO> queryTrack(String id) {
        if (provider == null) {
            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.BIZ_ERROR, "当前没有可用的快递查询供应商"));
        }
        DeliverBillPO deliverBillPO = findById(id);
        ApiResult apiResult = provider.query(deliverBillPO.getExpressComCode(), deliverBillPO.getExpressNo());

        if (apiResult.isSuccess()) {
            return (List<TrackInfoDTO>) apiResult.getData();
        }

        throw new ApiException(apiResult);
    }

    @Override
    public void receive(ReceiveInfo receiveInfo) {
        if (receiveInfo.getLogisticsStatus() == LogisticsStatus.RECEIVED) {
            //已签收
            DeliverBillPO deliverBillPO = deliverRepository.findTopByExpressComCodeAndExpressNo(receiveInfo.getExpressComCode(), receiveInfo.getExpressNo());
            this.receive(deliverBillPO);
        }
    }

    @Override
    public void receive(DeliverBillPO deliverBillPO) {
        deliverBillPO.setReceived(true);
        deliverBillPO.setReceivedTime(LocalDateTime.now());

        save(deliverBillPO);

        //修改orderItem的订单状态
        OrderEnums.DeliverStatus deliverStatus = OrderEnums.DeliverStatus.RECEIVED;
        if (deliverBillPO.getTo() == OrderEnums.DeliverRole.ORG_CENTER) {
            // TODO: 2020/6/15 鉴定中心的自动签收服务，咱不实现
        }
        //回写orderItem中的发货状态
        for (DeliverItemPO deliverItemPO : deliverBillPO.getDeliverItemPOS()) {
            orderItemRepository.updateDeliverStatus(deliverStatus, LocalDateTime.now(), deliverItemPO.getOrderItemId());
        }
        subOrderService.receive(deliverBillPO.getSubOrderId());
    }

    @Override
    public List<DeliverBillPO> findToReceive() {
        return deliverRepository.findByReceivedIsFalseAndLastReceivedTimeLessThanEqual(LocalDateTime.now());
    }

    @Override
    public void receiveByUser(String subOrderId) {
        SubOrderPO subOrderPO = subOrderService.findById(subOrderId);

        if (subOrderPO.isReceived()) {
            throw new ApiException(ApiResult.errorWith(ResultCodeEnum.ILLEGAL_STATE, "该订单已签收"));
        }
        //找到该订单所有的发货单
        List<DeliverBillPO> deliverBillPOS = deliverRepository.findBySubOrderId(subOrderId);
        for (DeliverBillPO deliverBillPO : deliverBillPOS) {
            receive(deliverBillPO);
        }
    }

    @Override
    public List<DeliverBillPO> findByOrder(String subOrderId) {
        return deliverRepository.findBySubOrderId(subOrderId);
    }

}
